package com.geo.geotool.service.geofile;

import com.geo.geotool.service.RemoveZFilter;
import lombok.extern.slf4j.Slf4j;
import org.geotools.data.*;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.MultiPolygon;
import org.locationtech.jts.geom.Polygon;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Description: 矢量文件管理类
 */
@Component
@Slf4j
public class ShapeFileManager {

    /**
     * Writes a SimpleFeatureCollection to a Shapefile.
     *
     * @param featureCollection The SimpleFeatureCollection to write.
     * @param outputPath        The output file path for the Shapefile.
     * @throws IOException If there is an error while writing the Shapefile.
     */
    public void saveToShapefile(SimpleFeatureCollection featureCollection, String outputPath) throws IOException {
        File outputFile = new File(outputPath);
        ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
        Map<String, Serializable> params = new HashMap<>();
        params.put("url", outputFile.toURI().toURL());
        params.put("create spatial index", Boolean.TRUE);

        ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
        SimpleFeatureType schema = featureCollection.getSchema();
        dataStore.createSchema(schema);
        // Set the charset for the Shapefile
        dataStore.setCharset(Charset.forName("UTF-8"));
        // Write the features to the Shapefile
        Transaction transaction = new DefaultTransaction("create");
        try {
            SimpleFeatureStore featureStore = (SimpleFeatureStore) dataStore.getFeatureSource();
            featureStore.setTransaction(transaction);
            featureStore.addFeatures(featureCollection);
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            throw e;
        } finally {
            transaction.close();
        }
    }


    public SimpleFeatureCollection redFromPostGis(String tableName, String cql) throws Exception {
        SimpleFeatureSource featureSource = dataStore.getFeatureSource(tableName);
        // 获取表中的所有要素
        // 构造CQL表达式，查询某日的最低温度
        SimpleFeatureCollection featureCollection = featureSource.getFeatures(CQL.toFilter(cql));
        return featureCollection;
    }

    /**
     * 保存矢量至PostGIS
     * reportDate 是新增一个Date类型字段，用于存储报告日期，后面开启时态GIS
     *
     * @param featureCollection 要保存的矢量集合
     * @param tableName         要保存的表名
     * @param params            PostGIS连接参数
     * @param reportDate        报告日期
     * @return
     * @throws IOException
     */
    @Autowired
    DataStore dataStore;

    public void saveToPostGIS(SimpleFeatureCollection featureCollection, String tableName, String reportDate) throws IOException, ParseException {
        // 创建要素类型
        SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
        builder.init(featureCollection.getSchema());
        builder.setName(tableName);
        if (featureCollection.getSchema().getDescriptor("reportDate") == null) {
            builder.add("reportDate", Date.class); // 添加reportDate字段
        }

        if (featureCollection.getSchema().getDescriptor("the_geom") == null) {
            builder.add("the_geom", Date.class); // 添加reportDate字段
        }
        SimpleFeatureType featureType = builder.buildFeatureType();
        // 如果指定的表不存在，则创建新表
        List<String> typeNames = Arrays.asList(dataStore.getTypeNames());
        if (!typeNames.contains(tableName)) {
            dataStore.createSchema(featureType);
        }

        // 获取要素存储
        SimpleFeatureStore featureStore = (SimpleFeatureStore) dataStore.getFeatureSource(tableName);
        // 创建要素集
        List<SimpleFeature> features = new ArrayList<>();
        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);

        // 读取SimpleFeatureCollection中的要素，并添加到要素集中
        try (SimpleFeatureIterator iterator = featureCollection.features()) {
            while (iterator.hasNext()) {
                SimpleFeature feature = iterator.next();
                if (reportDate != null) {
                    SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd");
                    featureBuilder.set("reportDate", sd.parse(reportDate));
                }
                featureBuilder.addAll(feature.getAttributes());
                SimpleFeature newFeature = featureBuilder.buildFeature(null);
                features.add(newFeature);
            }
        }
        // 将要素集保存到PostGIS数据库中
        Transaction transaction = new DefaultTransaction("create");
        featureStore.setTransaction(transaction);
        try {
            featureStore.addFeatures(DataUtilities.collection(features));
            transaction.commit();
        } catch (IOException e) {
            transaction.rollback();
            throw new RuntimeException(e);
        } finally {
            transaction.close();
        }
    }


    /**
     * 读取矢量文件
     *
     * @param filePath 矢量文件路径
     * @return
     * @throws IOException
     */
    public SimpleFeatureCollection readShapefile(String filePath) throws IOException {
        // 加载shapefile文件并创建FileDataStore对象
        File file = new File(filePath);
        FileDataStore store = FileDataStoreFinder.getDataStore(file);
        // 获取SimpleFeatureSource对象
        SimpleFeatureSource featureSource = store.getFeatureSource();
        // 获取SimpleFeatureCollection对象
        SimpleFeatureCollection featureCollection = featureSource.getFeatures();
        // 返回SimpleFeatureCollection对象
        return featureCollection;
    }


    /**
     * 两个矢量文件相交
     *
     * @param target              目标矢量集合
     * @param targetAttributes    目标矢量属性
     * @param intersect           交集矢量集合
     * @param intersectAttributes 交集矢量属性
     * @return
     */
    public SimpleFeatureCollection intersection(SimpleFeatureCollection target, String[] targetAttributes,
                                                SimpleFeatureCollection intersect, String[] intersectAttributes, String schemaName, Class geometryType) {
        // 根据目标矢量集合创建新的要素类型
        SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
        builder.init(target.getSchema());
        // 去除目标矢量不需要的属性
        if (targetAttributes != null && targetAttributes.length > 0) {
            List<AttributeDescriptor> targetList = target.getSchema().getAttributeDescriptors();
            for (AttributeDescriptor attributeDescriptor : targetList) {
                String attributeName = attributeDescriptor.getLocalName();
                if (!Arrays.asList(targetAttributes).contains(attributeName)) {
                    builder.remove(attributeName);
                }
            }
        }
        // 添加交集矢量的属性
        if (intersectAttributes != null && intersectAttributes.length > 0) {
            List<AttributeDescriptor> intersectList = intersect.getSchema().getAttributeDescriptors();
            for (AttributeDescriptor attributeDescriptor : intersectList) {
                String attributeName = attributeDescriptor.getLocalName();
                if (Arrays.asList(intersectAttributes).contains(attributeName)) {
                    builder.add(attributeName, attributeDescriptor.getType().getBinding());
                }
            }
        }
        // 添加the_geom字段
        if (builder.get("the_geom") == null) {
            builder.add("the_geom", geometryType);
        }
        // 设置新的要素类型名称
        builder.setName(schemaName);
        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(builder.buildFeatureType());

        DefaultFeatureCollection intersectionCollection = new DefaultFeatureCollection();
        try (SimpleFeatureIterator iterator1 = target.features()) {
            while (iterator1.hasNext()) {
                SimpleFeature feature1 = iterator1.next();
                Geometry geometry1 = (Geometry) feature1.getDefaultGeometry();
                try (SimpleFeatureIterator iterator2 = intersect.features()) {
                    while (iterator2.hasNext()) {
                        SimpleFeature feature2 = iterator2.next();
                        Geometry geometry2 = (Geometry) feature2.getDefaultGeometry();
                        RemoveZFilter removeZFilter = new RemoveZFilter();
                        geometry1.apply(removeZFilter);
                        geometry2.apply(removeZFilter);
                        if (geometry1.intersects(geometry2)) {
                            Geometry defaultGeometry = geometry1.intersection(geometry2);
                            if (defaultGeometry instanceof Polygon) {
                                // 将polygon转换为MultiPolygon
                                defaultGeometry = new MultiPolygon(new Polygon[]{(Polygon) defaultGeometry}, defaultGeometry.getFactory());
                            }
                            List<AttributeDescriptor> attributes = featureBuilder.getFeatureType().getAttributeDescriptors();
                            for (AttributeDescriptor attributeDescriptor : attributes) {
                                String attributeName = attributeDescriptor.getLocalName();
                                if (attributeName.equals("the_geom")) {
                                    featureBuilder.set("the_geom", defaultGeometry);
                                } else {
                                    if (Arrays.asList(targetAttributes).contains(attributeName)) {
                                        featureBuilder.set(attributeName, feature1.getAttribute(attributeName));
                                    } else if (Arrays.asList(intersectAttributes).contains(attributeName)) {
                                        featureBuilder.set(attributeName, feature2.getAttribute(attributeName));
                                    }
                                }
                            }
                            intersectionCollection.add(featureBuilder.buildFeature(null));
                        }
                    }
                }
            }
            return intersectionCollection;
        }
    }
}
